package com.javacv.plus.tools.benchmark;

import com.javacv.plus.core.metrics.ProcessorMetrics;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.Supplier;

/**
 * 处理器性能基准测试工具
 * 提供全面的性能测试和分析功能
 */
@Slf4j
public class ProcessorBenchmark {
    
    private final ExecutorService executorService;
    private final ProcessorMetrics metrics;
    
    public ProcessorBenchmark() {
        this.executorService = ForkJoinPool.commonPool();
        this.metrics = new ProcessorMetrics();
    }
    
    public ProcessorBenchmark(ExecutorService executorService) {
        this.executorService = executorService;
        this.metrics = new ProcessorMetrics();
    }
    
    /**
     * 执行单次基准测试
     */
    public <T> BenchmarkResult runSingleBenchmark(String testName, 
                                                 Supplier<T> testFunction) throws Exception {
        log.info("开始单次基准测试: {}", testName);
        
        long startTime = System.nanoTime();
        long startMemory = getUsedMemory();
        
        try {
            T result = testFunction.get();
            long endTime = System.nanoTime();
            long endMemory = getUsedMemory();
            
            long duration = (endTime - startTime) / 1_000_000; // 转换为毫秒
            long memoryUsage = endMemory - startMemory;
            
            BenchmarkResult benchmarkResult = new BenchmarkResult(
                testName, 1, duration, duration, duration, 
                memoryUsage, true, null
            );
            
            log.info("单次基准测试完成: {} - 耗时: {}ms, 内存: {}KB", 
                    testName, duration, memoryUsage / 1024);
            
            return benchmarkResult;
            
        } catch (Exception e) {
            long endTime = System.nanoTime();
            long duration = (endTime - startTime) / 1_000_000;
            
            BenchmarkResult benchmarkResult = new BenchmarkResult(
                testName, 1, duration, duration, duration, 
                0, false, e.getMessage()
            );
            
            log.error("单次基准测试失败: {} - 耗时: {}ms", testName, duration, e);
            return benchmarkResult;
        }
    }
    
    /**
     * 执行多次基准测试
     */
    public <T> BenchmarkResult runMultipleBenchmark(String testName, 
                                                   Supplier<T> testFunction,
                                                   int iterations) throws Exception {
        log.info("开始多次基准测试: {} - 迭代次数: {}", testName, iterations);
        
        List<Long> durations = new ArrayList<>();
        List<Long> memoryUsages = new ArrayList<>();
        int successCount = 0;
        String lastError = null;
        
        for (int i = 0; i < iterations; i++) {
            long startTime = System.nanoTime();
            long startMemory = getUsedMemory();
            
            try {
                T result = testFunction.get();
                long endTime = System.nanoTime();
                long endMemory = getUsedMemory();
                
                long duration = (endTime - startTime) / 1_000_000;
                long memoryUsage = endMemory - startMemory;
                
                durations.add(duration);
                memoryUsages.add(memoryUsage);
                successCount++;
                
                if ((i + 1) % 10 == 0) {
                    log.debug("基准测试进度: {}/{}", i + 1, iterations);
                }
                
            } catch (Exception e) {
                long endTime = System.nanoTime();
                long duration = (endTime - startTime) / 1_000_000;
                durations.add(duration);
                memoryUsages.add(0L);
                lastError = e.getMessage();
                log.warn("基准测试迭代 {} 失败: {}", i + 1, e.getMessage());
            }
        }
        
        // 计算统计信息
        long totalDuration = durations.stream().mapToLong(Long::longValue).sum();
        long avgDuration = totalDuration / iterations;
        long maxDuration = durations.stream().mapToLong(Long::longValue).max().orElse(0);
        long minDuration = durations.stream().mapToLong(Long::longValue).min().orElse(0);
        long avgMemory = memoryUsages.stream().mapToLong(Long::longValue).sum() / iterations;
        
        boolean success = successCount == iterations;
        
        BenchmarkResult result = new BenchmarkResult(
            testName, iterations, avgDuration, maxDuration, minDuration,
            avgMemory, success, lastError
        );
        
        result.setSuccessRate((double) successCount / iterations);
        result.setThroughput(iterations * 1000.0 / totalDuration); // 每秒操作数
        
        log.info("多次基准测试完成: {} - 平均耗时: {}ms, 成功率: {:.2f}%", 
                testName, avgDuration, result.getSuccessRate() * 100);
        
        return result;
    }
    
    /**
     * 执行并发基准测试
     */
    public <T> BenchmarkResult runConcurrentBenchmark(String testName,
                                                     Supplier<T> testFunction,
                                                     int threads,
                                                     int iterationsPerThread) throws Exception {
        log.info("开始并发基准测试: {} - 线程数: {}, 每线程迭代: {}", 
                testName, threads, iterationsPerThread);
        
        CountDownLatch startLatch = new CountDownLatch(1);
        CountDownLatch endLatch = new CountDownLatch(threads);
        
        List<Future<List<Long>>> futures = new ArrayList<>();
        long startTime = System.nanoTime();
        
        // 启动所有线程
        for (int i = 0; i < threads; i++) {
            Future<List<Long>> future = executorService.submit(() -> {
                List<Long> threadDurations = new ArrayList<>();
                try {
                    startLatch.await(); // 等待同时开始
                    
                    for (int j = 0; j < iterationsPerThread; j++) {
                        long iterStartTime = System.nanoTime();
                        testFunction.get();
                        long iterEndTime = System.nanoTime();
                        threadDurations.add((iterEndTime - iterStartTime) / 1_000_000);
                    }
                } catch (Exception e) {
                    log.error("并发测试线程异常", e);
                } finally {
                    endLatch.countDown();
                }
                return threadDurations;
            });
            futures.add(future);
        }
        
        // 开始测试
        startLatch.countDown();
        
        // 等待所有线程完成
        boolean completed = endLatch.await(5, TimeUnit.MINUTES);
        long endTime = System.nanoTime();
        
        if (!completed) {
            throw new TimeoutException("并发基准测试超时");
        }
        
        // 收集结果
        List<Long> allDurations = new ArrayList<>();
        for (Future<List<Long>> future : futures) {
            try {
                allDurations.addAll(future.get());
            } catch (Exception e) {
                log.error("获取并发测试结果失败", e);
            }
        }
        
        long totalTestTime = (endTime - startTime) / 1_000_000;
        int totalIterations = threads * iterationsPerThread;
        
        if (allDurations.isEmpty()) {
            return new BenchmarkResult(testName, totalIterations, 0, 0, 0, 0, false, "所有线程执行失败");
        }
        
        long avgDuration = allDurations.stream().mapToLong(Long::longValue).sum() / allDurations.size();
        long maxDuration = allDurations.stream().mapToLong(Long::longValue).max().orElse(0);
        long minDuration = allDurations.stream().mapToLong(Long::longValue).min().orElse(0);
        
        BenchmarkResult result = new BenchmarkResult(
            testName, totalIterations, avgDuration, maxDuration, minDuration,
            0, true, null
        );
        
        result.setConcurrentThreads(threads);
        result.setTotalTestTime(totalTestTime);
        result.setThroughput(totalIterations * 1000.0 / totalTestTime);
        
        log.info("并发基准测试完成: {} - 总耗时: {}ms, 吞吐量: {:.2f} ops/sec", 
                testName, totalTestTime, result.getThroughput());
        
        return result;
    }
    
    /**
     * 性能压力测试
     */
    public <T> StressTestResult runStressTest(String testName,
                                            Supplier<T> testFunction,
                                            Duration duration) throws Exception {
        log.info("开始压力测试: {} - 持续时间: {}秒", testName, duration.getSeconds());
        
        long endTime = System.currentTimeMillis() + duration.toMillis();
        List<Long> durations = new ArrayList<>();
        int successCount = 0;
        int failureCount = 0;
        long maxMemory = 0;
        
        while (System.currentTimeMillis() < endTime) {
            long startTime = System.nanoTime();
            long startMemory = getUsedMemory();
            
            try {
                testFunction.get();
                long iterEndTime = System.nanoTime();
                long iterDuration = (iterEndTime - startTime) / 1_000_000;
                
                durations.add(iterDuration);
                successCount++;
                
                long currentMemory = getUsedMemory();
                maxMemory = Math.max(maxMemory, currentMemory);
                
            } catch (Exception e) {
                failureCount++;
                log.debug("压力测试迭代失败: {}", e.getMessage());
            }
        }
        
        double avgDuration = durations.stream().mapToLong(Long::longValue).average().orElse(0.0);
        double successRate = (double) successCount / (successCount + failureCount);
        double throughput = (successCount + failureCount) * 1000.0 / duration.toMillis();
        
        StressTestResult result = new StressTestResult(
            testName, successCount, failureCount, avgDuration, 
            maxMemory, successRate, throughput
        );
        
        log.info("压力测试完成: {} - 成功: {}, 失败: {}, 成功率: {:.2f}%", 
                testName, successCount, failureCount, successRate * 100);
        
        return result;
    }
    
    /**
     * 生成基准测试报告
     */
    public void generateReport(List<BenchmarkResult> results) {
        String separator = new String(new char[80]).replace('\0', '=');
        String lineSeparator = new String(new char[40]).replace('\0', '-');
        
        log.info("\n" + separator);
        log.info("                           基准测试报告");
        log.info(separator);
        
        for (BenchmarkResult result : results) {
            log.info("\n测试名称: {}", result.getTestName());
            log.info("迭代次数: {}", result.getIterations());
            log.info("平均耗时: {}ms", result.getAvgDuration());
            log.info("最大耗时: {}ms", result.getMaxDuration());
            log.info("最小耗时: {}ms", result.getMinDuration());
            log.info("平均内存: {}KB", result.getAvgMemoryUsage() / 1024);
            log.info("测试结果: {}", result.isSuccess() ? "成功" : "失败");
            
            if (result.getSuccessRate() > 0) {
                log.info("成功率: {:.2f}%", result.getSuccessRate() * 100);
            }
            
            if (result.getThroughput() > 0) {
                log.info("吞吐量: {:.2f} ops/sec", result.getThroughput());
            }
            
            if (result.getConcurrentThreads() > 0) {
                log.info("并发线程: {}", result.getConcurrentThreads());
                log.info("总测试时间: {}ms", result.getTotalTestTime());
            }
            
            if (!result.isSuccess() && result.getErrorMessage() != null) {
                log.info("错误信息: {}", result.getErrorMessage());
            }
            
            log.info(lineSeparator);
        }
        
        log.info(separator);
    }
    
    private long getUsedMemory() {
        Runtime runtime = Runtime.getRuntime();
        return runtime.totalMemory() - runtime.freeMemory();
    }
    
    public void shutdown() {
        if (!executorService.isShutdown()) {
            executorService.shutdown();
        }
    }
    
    /**
     * 基准测试结果
     */
    @Data
    public static class BenchmarkResult {
        private final String testName;
        private final int iterations;
        private final long avgDuration;
        private final long maxDuration;
        private final long minDuration;
        private final long avgMemoryUsage;
        private final boolean success;
        private final String errorMessage;
        
        private double successRate;
        private double throughput;
        private int concurrentThreads;
        private long totalTestTime;
    }
    
    /**
     * 压力测试结果
     */
    @Data
    public static class StressTestResult {
        private final String testName;
        private final int successCount;
        private final int failureCount;
        private final double avgDuration;
        private final long maxMemoryUsage;
        private final double successRate;
        private final double throughput;
    }
} 